import os
import time
import zipfile
from subprocess import Popen
from threading import Thread, Lock

from src.base.settings import Settings
from src.logger import Logger
from src.conf.conf import Conf
from src.conf.backup import Backup
from src.conf.conf_manager import ConfManager
from src.process.base_pro_handler import BaseProHandler
from src.utils.method import Method


class ExeControl:
    def __init__(self, conf: ConfManager, logger: Logger):
        self.__control_path = Settings.WORK_PATH
        self.__ini: Conf = conf.conf
        self.__bak: Backup = conf.backup
        self.__logger = logger
        # 内部参数
        self.__server_exe: BaseProHandler
        self.__control_close = False
        self.__status_close = True
        self.exe_alive = False
        self.__exe_op_lock = Lock()
        self.__thread_list: list = []
        # 判断连续崩溃相关参数
        self.continue_start = True
        self.failed_times = 0
        self.record_time = time.time()

    # 启动服务器的实现
    def __exe_start_way(self) -> None:
        # 连续崩溃不再启动
        if not self.continue_start:
            return
        # 判断是否为ll服务器
        if self.__ini.exe_name == "bedrock_server.exe":
            if os.path.exists("bedrock_server_mod.exe"):
                print("[DLS] 检测到 bedrock_server_mod.exe")
                print("[DLS] 将为你自动启动 bedrock_server_mod.exe")
                self.__ini.exe_name = "bedrock_server_mod.exe"
        try:
            # 回到初始工作目录
            os.chdir(self.__control_path)
            if self.__ini.exe_dir:
                os.chdir(self.__ini.exe_dir)
            # 动态导入子进程模块
            if self.__ini.enable_pty or self.__ini.exe_name == "bedrock_server_mod.exe":
                from src.process.pty_handler import PtyHandler as ProHandler
            else:
                from src.process.subpro_handler import SubProHandler as ProHandler
            # 启动子进程
            self.__server_exe = ProHandler(self.__ini.exe_name)
        except Exception as err:
            self.__logger.add_log(f"[DLS][ERROR][EXE] 启动失败，请检查启动命令! {err}")
            return
        # 成功启动
        self.exe_alive = True
        self.__status_close = False
        self.__logger.add_log("[DLS][INFO][EXE] 服务器开始运行")
        # 服务器状态与重启判断
        while True:
            time.sleep(0.5)
            if self.__control_close:
                break
            # 开服、关服、备份时停止状态判断
            with self.__exe_op_lock:
                status_code = self.__server_exe.poll()
            # 是否需要继续状态判断，由外部控制
            if self.__status_close:
                break
            # 正常运行中返回码为None, 正常退出为0
            if status_code is None:
                continue
            if status_code == 0:
                self.exe_alive = False
                break
            self.__logger.add_log("\n[DLS][INFO][EXE] 检测到服务器崩溃\n")
            # 尝试关闭崩溃的服务器
            self.__exe_stop_way()
            # 未设置重启
            if not self.__ini.exe_re_alive:
                self.__logger.add_log("\n[DLS][WARN][EXE] 进程崩溃，未设置重启\n")
                break
            # 超出时限先重置计时与计数
            interval = time.time() - self.record_time
            if interval > self.__ini.exe_re_alive_limit:
                self.record_time = time.time()
                self.failed_times = 0
            # 对指定秒内崩溃进行计数
            self.failed_times += 1
            if self.failed_times > 2:
                self.__logger.add_log("\n[DLS][INFO][EXE] 发生连续崩溃，停止重启")
                self.failed_times = 0
                self.continue_start = False
                break
            # 进行重启
            self.__logger.add_log(f"\n[DLS][WARN][EXE]<{status_code}> 进程崩溃，将在5秒后重启\n")
            time.sleep(5)
            self.exe_start(auto_start=True)
            break

    # 关闭服务器的实现
    def __exe_stop_way(self):
        try:
            # 主动关闭服务器将关闭状态判断
            self.__status_close = True
            # 尝试正常关闭
            self.__server_exe.write(self.__ini.exe_stop_cmd)
            self.__server_exe.wait(self.__ini.exe_stop_wait)
        except OSError:
            # 崩溃后已自动关闭
            pass
        except Exception as e:
            # 关闭超时
            self.__logger.add_log(f"[DLS][WARN][EXE] 关闭服务器超时！{str(e)}")
            self.__logger.add_log("[DLS][INFO][EXE] 执行强制关闭...")
        self.__server_exe.terminate()
        self.__server_exe.kill()
        self.__server_exe.wait()
        self.exe_alive = False
        self.__logger.add_log("[DLS][INFO][EXE] 服务器停止运行")

    # 辅助线程：重定向输出
    def __output(self) -> None:
        while not self.__control_close:
            time.sleep(0.01)
            if self.exe_alive:
                next_line = self.__server_exe.readline()
                if next_line.strip() != "":
                    self.__logger.add_log(next_line)
                else:
                    # 读到空字符串意味着程序终止
                    time.sleep(0.2)

    # 辅助线程：时钟(定时任务)
    def __clock(self) -> None:
        while not self.__control_close:
            time.sleep(1)
            if self.__bak.on:
                now_day = time.strftime("%a", time.localtime())
                now_time = time.strftime("%H%M%S", time.localtime())
                if now_day in self.__bak.re_bak_day:
                    if now_time in self.__bak.re_bak_time:
                        self.__logger.add_cmd("startBak")

    # 重定向输入传入
    def execute_cmd(self, command: str, mute=False) -> bool:
        if self.exe_alive:
            try:
                # 命令执行
                self.__server_exe.write(command)
                return True
            except Exception as err:
                if not mute:
                    self.__logger.add_log(f"[DLS][WARN][CMD] 执行失败: {err}")
        elif command == "start":
            self.exe_start()
        else:
            if not mute:
                self.__logger.add_log("[DLS][INFO][CMD] 服务器未开启！")
        return False

    # 备份存档的实现
    def bak_world(self) -> None:
        zip_name = ""
        # 持锁关服备份
        with self.__exe_op_lock:
            # 记下备份时服务器状态，决定备份完后是否开服
            server_is_alive = self.exe_alive
            if server_is_alive:
                # 热备份不关服
                if self.__bak.bds_hot_backup:
                    self.__logger.file_hold = False
                    self.execute_cmd("save hold")
                    while not self.__logger.file_hold:
                        time.sleep(4)
                        self.execute_cmd("save query")
                        time.sleep(1)
                    print("[DLS] 热备份准备完毕")
                else:
                    self.__exe_stop_way()
            try:
                self.__logger.add_log("\n======>开始备份<======")
                self.__logger.add_log("[DLS][INFO][BAK] 服务器开始备份")
                # 先清理后备份时
                if self.__bak.clear_bak_first:
                    self.clear_world_bak(True)
                # 备份文件夹不存在则递归创建
                if not os.path.exists(self.__bak.bak_dir):
                    os.makedirs(self.__bak.bak_dir, exist_ok=True)
                # 设定压缩目录与压缩包名
                bak_time = time.strftime(self.__bak.bak_name, time.localtime())
                zip_name = os.path.join(self.__bak.bak_dir, f"{bak_time}")
                # 根据配置选择压缩方式
                start_time = time.time()
                tool_path = "C:/Program Files/7-Zip/7z.exe"
                if os.path.exists(tool_path):
                    zip_name += f".{self.__bak.compress_format}"
                    archive_cmd = f"\"{tool_path}\" a \"{zip_name}\""
                    at_least_one_dir = False
                    for world_dir in self.__bak.world_dirs:
                        if not os.path.exists(world_dir):
                            self.__logger.add_log(f"[DLS][INFO][BAK] 备份路径{world_dir}不存在！")
                            continue
                        at_least_one_dir = True
                        archive_cmd += f" \"{world_dir}\""
                    if at_least_one_dir:
                        Popen(archive_cmd, shell=False).wait()
                else:
                    zip_name += ".zip"
                    with zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED) as archive:
                        max_path_len = 0
                        for world_dir in self.__bak.world_dirs:
                            if not os.path.exists(world_dir):
                                self.__logger.add_log(f"[DLS][INFO][BAK] 备份路径{world_dir}不存在！")
                                continue
                            inner_dir = os.path.split(world_dir)[1]
                            for dir_path, _, filenames in os.walk(world_dir):
                                for filename in filenames:
                                    file_path = str(os.path.join(dir_path, filename))
                                    if len(file_path) > max_path_len:
                                        max_path_len = len(file_path)
                                    # 显示压缩进度
                                    print(f"\r正在压缩: {file_path.ljust(max_path_len)}", end="")
                                    target_path = inner_dir + file_path.replace(world_dir, "")
                                    archive.write(file_path, target_path)
                        print("")
                # 输出结果信息
                end_time = time.time()
                self.__logger.add_log(f"[DLS] 压缩完毕，耗时{format(end_time - start_time, ".2f")}s")
                if os.path.exists(zip_name):
                    memory_size: str = Method.parse_memory_size(os.path.getsize(zip_name))
                    self.__logger.add_log(f"[DLS][INFO][BAK] 备份保存至: {zip_name}")
                    self.__logger.add_log(f"[DLS][INFO][BAK] 服务器备份完毕，存档大小{memory_size}")
                else:
                    self.__logger.add_log(f"[DLS][INFO][BAK] 备份失败，请检查备份路径！")
                # 清理存档
                if not self.__bak.clear_bak_first:
                    self.clear_world_bak()
            except Exception as err:
                self.__logger.add_log(f"[DLS][INFO][BAK] 备份异常: {err}")
                # 删除无效的备份
                if os.path.exists(zip_name) and os.path.isfile(zip_name):
                    os.remove(zip_name)
                    self.__logger.add_log(f"[DLS][INFO][BAK] 无效的备份已清理")
        self.__logger.add_log("======>备份完毕<======\n")
        # 备份完成后处理
        if server_is_alive:
            if self.__bak.bds_hot_backup:
                self.execute_cmd("save resume")
            else:
                self.exe_start()

    # 清理存档的实现
    def clear_world_bak(self, clear_first=False) -> None:
        # 未启用不执行
        if self.__bak.bak_limit_num <= 0:
            return
        try:
            bak_files: list = [os.path.join(self.__bak.bak_dir, file)
                               for file in os.listdir(self.__bak.bak_dir)]
            over_limit_num = len(bak_files) - self.__bak.bak_limit_num
            # 先清理后备份时
            if clear_first:
                over_limit_num += 1
            # 未超出限制不执行
            if over_limit_num <= 0:
                return
            bak_files_date: list = [os.path.getctime(file) for file in bak_files]
            sorted_date: list = bak_files_date.copy()
            sorted_date.sort()
            for i in range(over_limit_num):
                os.remove(bak_files[bak_files_date.index(sorted_date[i])])
        except Exception as err:
            self.__logger.add_log(f"[DLS][INFO][BAK] 备份清理异常: {err}")

    # 线程生成与管理
    def __my_thread(self, func) -> None:
        th = Thread(target=func, daemon=True)
        th.start()
        self.__thread_list.append(th)

    # 单独启动服务器
    def exe_start(self, mute=False, auto_start=False) -> None:
        # 互斥锁保证代码不同时运行
        with self.__exe_op_lock:
            if not self.exe_alive:
                if not auto_start:
                    self.continue_start = True
                self.__my_thread(self.__exe_start_way)
            else:
                if not mute:
                    self.__logger.add_log("[DLS][INFO][CMD] 服务器已开启!")

    # 单独关闭服务器
    def exe_stop(self, mute=False) -> None:
        with self.__exe_op_lock:
            if not self.exe_alive:
                if not mute:
                    self.__logger.add_log("[DLS][INFO][CMD] 服务器已关闭!")
            else:
                self.__exe_stop_way()

    # 单独重启服务器
    def exe_restart(self) -> None:
        self.exe_stop(mute=True)
        self.exe_start()

    # 等待线程运行
    def wait(self) -> None:
        for th in self.__thread_list:
            try:
                th.join(self.__ini.exe_stop_wait + 1)
            except Exception as e:
                print(f"\033[31m[DLS][ERROR] {th}异常的线程关闭！错误：{e}\033[0m")
        # 清空线程列表
        self.__thread_list.clear()

    # 启动服务器包括监听器
    def start_all(self) -> None:
        self.__control_close = False
        # 默认启动设定为true时，立即开启服务器
        if self.__ini.default_start:
            self.exe_start()
        else:
            self.__logger.add_log("[DLS] 启动时开服已设置为false，服务器不会立即启动。要启动服务器，请手动执行start命令。")
        self.__my_thread(self.__output)
        self.__my_thread(self.__clock)

    # 关闭服务器包括监听器
    def stop_all(self) -> None:
        self.exe_stop(mute=True)
        self.__control_close = True
        self.wait()
